using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Comp376A2_9609695
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D dirt;
        Texture2D DigDugImg;
        Texture2D MonsterImg;
        Texture2D PumpImg;
        Texture2D tunnels;
        Texture2D numbers;
        Texture2D other;

        SpriteSheet numberSheet;
        SpriteSheet DigDugSheet;
        SpriteSheet PookaSheet;
        SpriteSheet FygarSheet;
        SpriteSheet PumpSheet;
        SpriteSheet MiscSheet;

        DigDug DigDugObj;
        Score score;
        Score highScore;
        Score levelNum;

        GameController control;
        int elapsed;
        int blinkTimer;
        int pumpTimer;
        int respawnTimer;
        bool blinkState = false;

        MainMenu menu;

        AudioEngine audioEngine;
        WaveBank waveBank;
        SoundBank soundBank;

        bool pausePress = false;
        bool pumpPress = false;

        SpriteFont font;
        SpriteFont boldfont;

        Cue musicCue = null;
        Cue monsterLoop = null;

        int Width = 256;
        int Height = 224;
        float scale = 2.0f;

        Rectangle field = new Rectangle(8, 16, 192, 208);
        Level level;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferWidth = Width;
            graphics.PreferredBackBufferHeight = Height;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here 
            audioEngine = new AudioEngine(@"Content\Sound\digdugsfx.xgs");

            //  Assume the default names for the wave and sound bank.  
            //   To change these names, change properties in XACT
            waveBank = new WaveBank(audioEngine, @"Content\Sound\Wave Bank.xwb");
            soundBank = new SoundBank(audioEngine, @"Content\Sound\Sound Bank.xsb");

            font = Content.Load<SpriteFont>("font");
            boldfont = Content.Load<SpriteFont>("boldfont");

            musicCue = soundBank.GetCue("digsong");
            monsterLoop = soundBank.GetCue("monsterloop");
            monsterLoop.Play(); monsterLoop.Pause();

            base.Initialize();
        }

        /// <summary>
        /// Resets all of the game's data. Called at first initialization and then on every "game over."
        /// </summary>
        public void reset()
        {
            control = new GameController();
            DigDugObj = new DigDug(DigDugSheet);
            DigDugObj.animation = "left";
            DigDugObj.soundbank = soundBank;

            score = new Score(numberSheet);
            score.position = new Vector2(240, 64);
            score.value = 0;

            if (highScore == null)
            {
                highScore = new Score(numberSheet);
                highScore.value = 5000;
            }
            highScore.position = new Vector2(240, 32);

            menu = new MainMenu(MiscSheet, boldfont, highScore);
            menu.position.Y = Height;

            levelNum = new Score(numberSheet);
            levelNum.position = new Vector2(240, 208);
            levelNum.value = 1;

            level = new Level(tunnels);
            level.setField(field);

            control.addObject(level);
            control.addObject(DigDugObj);
            control.addObject(score);
            control.addObject(highScore);
            control.addObject(levelNum);

            control.player = DigDugObj;
            control.setScoreObject(score);
            control.font = font;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            dirt = Content.Load<Texture2D>("Sprites/dirt");
            DigDugImg = Content.Load<Texture2D>("Sprites/digdug");
            PumpImg = Content.Load<Texture2D>("Sprites/pump");
            MonsterImg = Content.Load<Texture2D>("Sprites/enemies");
            tunnels = Content.Load<Texture2D>("Sprites/tunnels");
            numbers = Content.Load<Texture2D>("Sprites/numbers");
            other = Content.Load<Texture2D>("Sprites/other");

            spriteBatch = new SpriteBatch(GraphicsDevice);

            DigDugSheet = new SpriteSheet(DigDugImg);
            initDigDugSheet(DigDugSheet);

            PookaSheet = new SpriteSheet(MonsterImg);
            initPookaSheet(PookaSheet);

            FygarSheet = new SpriteSheet(MonsterImg);
            initFygarSheet(FygarSheet);

            PumpSheet = new SpriteSheet(PumpImg);
            initPumpSheet(PumpSheet);

            numberSheet = new SpriteSheet(numbers);
            initNumberSheet(numberSheet);

            MiscSheet = new SpriteSheet(other);
            initMiscSheet(MiscSheet);
            reset();

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            KeyboardState keyState = Keyboard.GetState();
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || keyState.IsKeyDown(Keys.Escape))
                this.Exit();

            // Scale the window to the appropriate size
            graphics.PreferredBackBufferWidth = (int)(Width * scale);
            graphics.PreferredBackBufferHeight = (int)(Height * scale);
            graphics.ApplyChanges();

            elapsed = gameTime.ElapsedGameTime.Milliseconds;
            blinkTimer += elapsed;

            //Console.WriteLine(control.level.getCell(3, 4));
            // A timer for anything that blinks.
            if (blinkTimer >= 250)
            {
                blinkState = !blinkState;
                blinkTimer = 0;
            }

            // The number keys let you resize the screen from 1x to 4x
            if (keyState.IsKeyDown(Keys.D1))
            {
                scale = 1.0f;
            }
            if (keyState.IsKeyDown(Keys.D2))
            {
                scale = 2.0f;
            }
            if (keyState.IsKeyDown(Keys.D3))
            {
                scale = 3.0f;
            }
            if (keyState.IsKeyDown(Keys.D4))
            {
                scale = 4.0f;
            }

            monsterLoop.Pause();

            // Determine the state of the game
            switch(control.state)
            {
                case GameController.GameState.Menu:
                    // At the main menu, scroll the screen upward, then allow the player to start the game.
                    if (menu.position.Y > 0)
                        menu.position.Y -= 16f / (elapsed+1);
                    else
                        menu.position.Y = 0;
                    if ((keyState.IsKeyDown(Keys.Enter) || keyState.IsKeyDown(Keys.Space)) && !pausePress)
                    {
                        pausePress = true; // avoids repeat presses

                        if (menu.position.Y > 0)
                            menu.position.Y = 0;
                        else
                        {
                            control.level = new Level(tunnels);
                            control.level.setField(field);
                            initLevel1(control.level);
                            levelNum.value = 1;
                            control.lives = 3;
                            highScore.position = new Vector2(240, 32);
                            control.state = GameController.GameState.LevelStart;
                        }
                    }
                    if ((keyState.IsKeyUp(Keys.Enter) && keyState.IsKeyUp(Keys.Space)) && pausePress)
                        pausePress = false;
                    break;
                case GameController.GameState.LevelStart:
                    // At the start of every level, DigDug starts at the top-right and automatically digs to the middle.
                    if (musicCue.Name != "levelstart")
                    {
                        musicCue = soundBank.GetCue("levelstart");
                        musicCue.Play();
                        control.player.position = new Vector2(control.level.X + 176, control.level.Y);
                    }
                    if (DigDugObj.position.Y == (0 + control.level.Y) && DigDugObj.position.X > (96 + control.level.X))
                    {
                        control.player.move(DigDug.Direction.Left, control);
                    }
                    else if (DigDugObj.position.Y < (96 + control.level.Y))
                    {
                        control.player.move(DigDug.Direction.Down, control);
                    }
                    else if ((int)control.player.position.X == (80 + control.level.X) && (int)control.player.position.Y == (96 + control.level.Y))
                    {
                        control.player.faceDir(DigDug.Direction.Right);
                        control.player.animation = "right";
                    }

                    // When the music is done and DigDug is in the right place the game is ready to start.
                    if (!musicCue.IsPlaying && (int)control.player.position.X == (80 + control.level.X) && (int)control.player.position.Y == (96 + control.level.Y))
                    {
                        control.state = GameController.GameState.Game;
                        control.player.state = DigDug.State.Moving;
                        musicCue = soundBank.GetCue("digsong");
                        musicCue.Stop(AudioStopOptions.Immediate);
                    }
                    control.player.update(gameTime, control);
                    break;
                case GameController.GameState.Game:
                    // During the main game state, input is accepted and every object that updates does so
                    if (!musicCue.IsPlaying)
                    {
                        musicCue = soundBank.GetCue("digsong");
                        musicCue.Play();
                    }

                    // Enter or P pause the game. If the window loses focus, it also pauses
                    if ((keyState.IsKeyDown(Keys.Enter) || keyState.IsKeyDown(Keys.P) || !IsActive) && !pausePress)
                    {
                        control.state = GameController.GameState.Paused;
                        pausePress = true;
                    }
                    if (keyState.IsKeyUp(Keys.Enter) && keyState.IsKeyUp(Keys.P) && pausePress)
                        pausePress = false;

                    if (control.player.state == DigDug.State.Pumping)
                    {
                        pumpTimer += elapsed;
                    }

                    // There is still some game time while DigDug is squished so the state must be checked for
                    if (control.player.state != DigDug.State.Squished)
                    {
                        // Spacebar throws the pump
                        if (keyState.IsKeyDown(Keys.Space))
                        {
                            // If DigDug is already pumping, pump some more
                            if (control.player.state == DigDug.State.Pumping && !pumpPress)
                            {
                                if (pumpTimer > 200)
                                {
                                    pumpTimer -= 200;
                                    control.player.pump();
                                    pumpPress = true; // Makes sure the spacebar must be tapped, not held
                                }
                            }
                            // Otherwise, throw the pump
                            else if ((control.player.state == DigDug.State.Moving || control.player.state == DigDug.State.Digging) && !pumpPress)
                            {
                                control.player.throwPump(control, PumpSheet);
                                pumpTimer = 0;
                                pumpPress = true;
                            }
                        }

                        if (keyState.IsKeyUp(Keys.Space) && pumpPress)
                        {
                            pumpPress = false;
                        }
                        // W, A, S and D move DigDug. While he is moving, the theme plays.
                        if (control.player.state != DigDug.State.Throwing)
                        {
                            bool playSong = false;
                            if (keyState.IsKeyDown(Keys.W))
                            {
                                playSong = true;
                                control.player.move(DigDug.Direction.Up, control);
                            }
                            else if (keyState.IsKeyDown(Keys.S))
                            {
                                control.player.move(DigDug.Direction.Down, control);
                                playSong = true;
                            }
                            else if (keyState.IsKeyDown(Keys.A))
                            {
                                control.player.move(DigDug.Direction.Left, control);
                                playSong = true;
                            }
                            else if (keyState.IsKeyDown(Keys.D))
                            {
                                control.player.move(DigDug.Direction.Right, control);
                                playSong = true;
                            }

                            if (!playSong && !musicCue.IsStopped)
                                musicCue.Pause();
                            else
                                musicCue.Resume();
                        }
                    }
                    // If the main theme isn't playing, the monsters make their noise instead.
                    if (!musicCue.IsPlaying || musicCue.IsPaused)
                        monsterLoop.Resume();
                    else
                        monsterLoop.Pause();

                    // The game controller updates all of the objects
                    control.updateAll(gameTime);
                    if (control.score > highScore.value)
                        highScore.value = control.score;
                    break;
                case GameController.GameState.LoseLife:
                    // When a life is lost, a small theme plays, the player and monsters are reset and the game continues
                    if (musicCue.Name != "digdughit" && musicCue.Name != "digdugdie")
                    {
                        control.lives--;
                        control.updateAll(gameTime);
                        respawnTimer = 0;
                        musicCue.Stop(AudioStopOptions.Immediate);
                        musicCue = soundBank.GetCue("digdughit");
                        musicCue.Play();
                    }
                    if (!musicCue.IsPlaying && musicCue.Name == "digdughit")
                    {
                        control.player.state = DigDug.State.Dead;
                        musicCue = soundBank.GetCue("digdugdie");
                        musicCue.Play();
                    }
                    if (!musicCue.IsPlaying && musicCue.Name == "digdugdie")
                    {
                        // Only continue the game if the player still has lives. Game Over otherwise.
                        if (control.lives > 0)
                        {
                            respawnTimer += elapsed;
                            control.player.position = new Vector2(80 + control.level.X, 96 + control.level.Y);
                            control.player.state = DigDug.State.Start;
                            control.player.faceDir(DigDug.Direction.Right);
                            control.player.animation = "right";
                            control.resetMonsters();
                        }
                        else
                        {
                            control.state = GameController.GameState.GameOver;
                        }
                    }
                    // As short pause at the start
                    if (respawnTimer >= 1500)
                    {
                        control.state = GameController.GameState.Game;
                        control.player.state = DigDug.State.Moving;
                    }
                    control.player.update(gameTime, control);
                    break;
                case GameController.GameState.Paused:
                    // When the game is paused, nothing much happens. P or Enter will unpause the game
                    if ((keyState.IsKeyDown(Keys.Enter) || keyState.IsKeyDown(Keys.P)) && !pausePress)
                    {
                        control.state = GameController.GameState.Game;
                        pausePress = true;
                    }
                    if (keyState.IsKeyUp(Keys.Enter) && keyState.IsKeyUp(Keys.P) && pausePress)
                        pausePress = false;
                    break;
                case GameController.GameState.LevelDone:
                    // When the level is done, a tune is played and the player goes to the next level
                    if (musicCue.Name != "levelover")
                    {
                        control.updateAll(gameTime);
                        if (musicCue.IsPlaying)
                            musicCue.Stop(AudioStopOptions.Immediate);
                        musicCue = soundBank.GetCue("levelover");
                        musicCue.Play();
                    }
                    if (musicCue.Name == "levelover" && !musicCue.IsPlaying)
                    {
                        // There are only two levels so the game alternates between them
                        control.nextLevel();
                        if (control.currLevel % 2 == 1)
                        {
                            control.level = new Level(tunnels);
                            control.level.setField(field);
                            initLevel1(control.level);
                        }
                        if (control.currLevel % 2 == 0)
                        {
                            control.level = new Level(tunnels);
                            control.level.setField(field);
                            initLevel2(control.level);
                        }
                        control.state = GameController.GameState.LevelStart;
                        levelNum.value = control.currLevel;
                    }
                    break;
                case GameController.GameState.GameOver:
                    // At a Game Over, a tune is played and the game is reset.
                    if (musicCue.Name != "digdughit" && musicCue.Name != "digdugdie" && musicCue.Name != "gameover")
                    {
                        musicCue.Stop(AudioStopOptions.Immediate);
                        musicCue = soundBank.GetCue("digdughit");
                        musicCue.Play();
                    }
                    if (!musicCue.IsPlaying && musicCue.Name == "digdughit")
                    {
                        control.player.state = DigDug.State.Dead;
                        musicCue = soundBank.GetCue("digdugdie");
                        musicCue.Play();
                    }
                    if (!musicCue.IsPlaying && musicCue.Name == "digdugdie")
                    {
                        control.player.state = DigDug.State.Dead;
                        musicCue = soundBank.GetCue("gameover");
                        musicCue.Play();
                    }
                    if (musicCue.Name == "gameover" && !musicCue.IsPlaying)
                    {
                        reset();
                    }
                    break;
                default:
                    break;
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            // TODO: Add your drawing code here
            //spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone, null, Matrix.CreateScale(scale));
            spriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(scale));
            if (control.state == GameController.GameState.Menu)
            {
                // At the main menu, only the menu is drawn.
                menu.draw(spriteBatch);
            }
            else
            {
                Rectangle source = new Rectangle(0, 0, 16, 16);
                Rectangle dest;
                // The ground has three tints. The third is controlled by the shade of grey in the dirt sprite
                Color tint1 = new Color(255, 173, 37);
                Color tint2 = new Color(192, 130, 28);
                Color layerTint = tint1;

                // At every layer, the dirt switches between "light" and "dark". Halfway down the tint darkens.
                for (int j = 1; j != field.Height / 16; j++)
                {
                    if (j == 4)
                    {
                        source = new Rectangle(16, 0, 16, 16);
                    }
                    if (j == 7)
                    {
                        source = new Rectangle(0, 0, 16, 16);
                        layerTint = tint2;
                    }
                    if (j == 10)
                    {
                        source = new Rectangle(16, 0, 16, 16);
                    }
                    for (int i = 0; i != field.Width / 16; i++)
                    {
                        dest = new Rectangle(16 * i + field.X, 16 * j + field.Y, 16, 16);
                        spriteBatch.Draw(dirt, dest, source, layerTint);
                    }
                }

                // Flowers appear at the top of the level. A big one appears for every 10 levels
                int flowerInd = MiscSheet.getAnimation("flower").First();
                Rectangle flower = MiscSheet.getSprite(flowerInd);
                for (int i = 0; i < levelNum.value % 10; i++)
                {
                    spriteBatch.Draw(MiscSheet.image, new Vector2(192 - 16 * i - level.X, level.Y), flower, Color.White);
                }

                int bigflowerInd = MiscSheet.getAnimation("bigflower").First();
                Rectangle bigflower = MiscSheet.getSprite(bigflowerInd);
                for (int i = 0; i < levelNum.value / 10; i++)
                {
                    spriteBatch.Draw(MiscSheet.image, new Vector2(192 - 16 * i - 16 * (levelNum.value % 10) - level.X, level.Y - 16), bigflower, Color.White);
                }

                // Everything that can be drawn is drawn by the controller.
                control.drawAll(spriteBatch);

                // The HUD is drawn here. Score, lives, level and some text
                Color red = new Color(181, 49, 32);
                if (control.state == GameController.GameState.LoseLife && control.player.state == DigDug.State.Start)
                    spriteBatch.DrawString(boldfont, "READY!", new Vector2(72, 128), Color.White);
                
                if (control.state == GameController.GameState.GameOver)
                    spriteBatch.DrawString(boldfont, "GAME OVER!", new Vector2(56, 128), Color.White);

                int livesInd = MiscSheet.getAnimation("life").First();
                Rectangle lives = MiscSheet.getSprite(livesInd);
                for(int i=1; i<control.lives; i++)
                {
                    spriteBatch.Draw(MiscSheet.image, new Vector2(192 + 16 * i - level.X, 128), lives, Color.White);
                }

                spriteBatch.DrawString(boldfont, "HI-", new Vector2(202, 16), red);
                spriteBatch.DrawString(boldfont, " SCORE", new Vector2(202, 23), red);
                spriteBatch.DrawString(boldfont, "ROUND", new Vector2(202, 198), Color.White);

                if (blinkState && control.state == GameController.GameState.Paused)
                    spriteBatch.DrawString(boldfont, "PAUSE", new Vector2(202, 118), Color.White);

                if (blinkState || (control.state != GameController.GameState.Game && control.state != GameController.GameState.Menu))
                    spriteBatch.DrawString(boldfont, "1UP", new Vector2(202, 54), red);
            }
            spriteBatch.End();
            base.Draw(gameTime);
        }

        /// <summary>
        /// The spritesheet for DigDug is split and initialized.
        /// </summary>
        /// <param name="sheet">The sheet to initialize</param>
        protected void initDigDugSheet(SpriteSheet sheet)
        {
            sheet.splitSheet(6, 6, 36);
            // Walking
            sheet.addAnimation("left", new int[] { 0, 1 });
            sheet.addAnimation("up", new int[] { 2, 3 });
            sheet.addAnimation("right", new int[] { 4, 5 });
            sheet.addAnimation("down", new int[] { 6, 7 });
            // Digging
            sheet.addAnimation("digleft", new int[] { 8, 9 });
            sheet.addAnimation("digup", new int[] { 10, 11 });
            sheet.addAnimation("digright", new int[] { 12, 13 });
            sheet.addAnimation("digdown", new int[] { 14, 15 });
            // Pumping
            sheet.addAnimation("throwleft", new int[] { 18 } );
            sheet.addAnimation("pumpleft", new int[] { 16, 17 });
            sheet.addAnimation("throwup", new int[] { 21 });
            sheet.addAnimation("pumpup", new int[] { 20, 19 });
            sheet.addAnimation("throwright", new int[] { 24 });
            sheet.addAnimation("pumpright", new int[] { 23, 22 });
            sheet.addAnimation("throwdown", new int[] { 27 });
            sheet.addAnimation("pumpdown", new int[] { 26, 25 });
            // Death
            sheet.addAnimation("dead", new int[] { 28, 29, 30, 31, 32, 33, 34 });

        }

        /// <summary>
        /// The spritesheet for Pookas is split and initialized.
        /// </summary>
        /// <param name="sheet">The sheet to initialize</param>
        protected void initPookaSheet(SpriteSheet sheet)
        {
            // Walk
            sheet.addSprite(new Rectangle(32,0,16,16));
            sheet.addSprite(new Rectangle(48,0,16,16));
            // Ghost
            sheet.addSprite(new Rectangle(0,0,16,16));
            sheet.addSprite(new Rectangle(16,0,16,16));
            // Pump
            sheet.addSprite(new Rectangle(0, 48, 16, 21));
            sheet.addSprite(new Rectangle(16, 48, 22, 21));
            sheet.addSprite(new Rectangle(38, 48, 23, 21));
            sheet.addSprite(new Rectangle(61, 48, 26, 21));

            sheet.addAnimation("walk", new int[] { 0, 1 });
            sheet.addAnimation("ghost", new int[] { 2, 3 });
            sheet.addAnimation("pump", new int[] { 4, 5, 6, 7 });
        }

        /// <summary>
        /// The spritesheet for Fygars is split and initialized.
        /// </summary>
        /// <param name="sheet">The sheet to initialize</param>
        protected void initFygarSheet(SpriteSheet sheet)
        {
            // Walk
            sheet.addSprite(new Rectangle(32, 16, 16, 16));
            sheet.addSprite(new Rectangle(48, 16, 16, 16));
            // Ghost
            sheet.addSprite(new Rectangle(0, 16, 16, 16));
            sheet.addSprite(new Rectangle(16, 16, 16, 16));
            // Pump
            sheet.addSprite(new Rectangle(0, 70, 16, 23));
            sheet.addSprite(new Rectangle(16, 70, 22, 23));
            sheet.addSprite(new Rectangle(38, 70, 23, 23));
            sheet.addSprite(new Rectangle(61, 70, 26, 23));
            // Fire
            sheet.addSprite(new Rectangle(0, 32, 16, 16));
            sheet.addSprite(new Rectangle(16, 32, 16, 16));
            sheet.addSprite(new Rectangle(32, 32, 32, 16));
            sheet.addSprite(new Rectangle(64, 32, 48, 16));

            sheet.addAnimation("walk", new int[] { 0, 1 });
            sheet.addAnimation("ghost", new int[] { 2, 3 });
            sheet.addAnimation("pump", new int[] { 4, 5, 6, 7 });
            sheet.addAnimation("fire", new int[] { 8 });
            sheet.addAnimation("flame", new int[] { 9, 10, 11 });
        }

        /// <summary>
        /// The spritesheet for the pump is split and initialized.
        /// </summary>
        /// <param name="sheet">The sheet to initialize</param>
        protected void initPumpSheet(SpriteSheet sheet)
        {
            // Horizontal and Vertical hoses
            sheet.addSprite(new Rectangle(0, 0, 16, 8));
            sheet.addSprite(new Rectangle(16, 0, 8, 16));
            // Left, Up, Right, Down tips
            sheet.addSprite(new Rectangle(0, 8, 3, 8));
            sheet.addSprite(new Rectangle(6, 8, 8, 3));
            sheet.addSprite(new Rectangle(3, 8, 3, 8));
            sheet.addSprite(new Rectangle(6, 11, 8, 5));
        }

        /// <summary>
        /// The spritesheet for miscellanea is split and initialized.
        /// </summary>
        /// <param name="sheet">The sheet to initialize</param>
        protected void initMiscSheet(SpriteSheet sheet)
        {
            // Rock
            sheet.addSprite(new Rectangle(0, 0, 16, 16));
            sheet.addSprite(new Rectangle(16, 0, 16, 16));
            sheet.addSprite(new Rectangle(0, 16, 16, 16));
            sheet.addSprite(new Rectangle(16, 16, 16, 16));

            // Flowers
            sheet.addSprite(new Rectangle(16, 32, 16, 16));
            sheet.addSprite(new Rectangle(32, 16, 16, 32));

            // Life
            sheet.addSprite(new Rectangle(32, 0, 16, 16));

            // Start pointer
            sheet.addSprite(new Rectangle(0, 32, 16, 16));

            // Logo
            sheet.addSprite(new Rectangle(48, 0, 156, 48));

            sheet.addAnimation("rocksteady", new int[] { 0 });
            sheet.addAnimation("rockshake", new int[] { 0, 1 });
            sheet.addAnimation("rockbreak", new int[] { 2, 3 });
            sheet.addAnimation("flower", new int[] { 4 });
            sheet.addAnimation("bigflower", new int[] { 5 });
            sheet.addAnimation("life", new int[] { 6 });
            sheet.addAnimation("pointer", new int[] { 7 });
            sheet.addAnimation("logo", new int[] { 8 });
        }

        /// <summary>
        /// The spritesheet for numbers is split and initialized.
        /// </summary>
        /// <param name="sheet">The sheet to initialize</param>
        protected void initNumberSheet(SpriteSheet sheet)
        {
            sheet.splitSheet(2, 5, 10);
        }

        /// <summary>
        /// The first level is initialized. Tunnels are dug and monsters and rocks are placed.
        /// </summary>
        /// <param name="level">The level object to initialize</param>
        protected void initLevel1(Level level)
        {
            level.setCell(1, 2, 8);
            for (int j = 3; j != 5; j++)
            {
                level.setCell(1, j, 10);
            }
            level.setCell(1, 5, 2);

            level.setCell(8, 7, 8);
            for (int j = 8; j != 11; j++)
            {
                level.setCell(8, j, 10);
            }
            level.setCell(8, 11, 2);

            level.setCell(8, 2, 4);
            for (int i = 9; i != 10; i++)
            {
                level.setCell(i, 2, 5);
            }
            level.setCell(10, 2, 1);

            level.setCell(1, 8, 4);
            for (int i = 2; i != 4; i++)
            {
                level.setCell(i, 8, 5);
            }
            level.setCell(4, 8, 1);

            level.setCell(4, 6, 4);
            level.setCell(6, 6, 1);


            Pooka p1 = new Pooka(level.X + 16, level.Y + 48);
            p1.spritesheet = PookaSheet;
            p1.animation = "walk";
            p1.soundbank = soundBank;

            Pooka p2 = new Pooka(level.X + 160, level.Y + 32);
            p2.spritesheet = PookaSheet;
            p2.animation = "walk";
            p2.soundbank = soundBank;

            Pooka p3 = new Pooka(level.X + 128, level.Y + 160);
            p3.spritesheet = PookaSheet;
            p3.animation = "walk";
            p3.soundbank = soundBank;

            Fygar f1 = new Fygar(level.X + 32, level.Y + 128);
            f1.spritesheet = FygarSheet;
            f1.animation = "walk";
            f1.soundbank = soundBank;
            f1.facing = Monster.Direction.Right;
            
            control.addObject(p1);
            control.addObject(p2);
            control.addObject(p3);
            control.addObject(f1);

            Rock r1 = new Rock(level.X + 48, level.Y + 48, MiscSheet);
            r1.animation = "rocksteady";
            r1.soundbank = soundBank;

            Rock r2 = new Rock(level.X + 32, level.Y + 160, MiscSheet);
            r2.animation = "rocksteady";
            r2.soundbank = soundBank;

            Rock r3 = new Rock(level.X + 144, level.Y + 128, MiscSheet);
            r3.animation = "rocksteady";
            r3.soundbank = soundBank;

            control.addObject(r1);
            control.addObject(r2);
            control.addObject(r3);
        }

        /// <summary>
        /// The second level is initialized. Tunnels are dug and monsters and rocks are placed.
        /// </summary>
        /// <param name="level">The level object to initialize</param>
        protected void initLevel2(Level level)
        {
            level.setCell(8, 3, 4);
            level.setCell(9, 3, 5);
            level.setCell(10, 3, 1);

            level.setCell(1, 5, 4);
            level.setCell(2, 5, 5);
            level.setCell(3, 5, 1);

            level.setCell(5, 8, 4);
            level.setCell(6, 8, 5);
            level.setCell(7, 8, 1);

            level.setCell(8, 10, 4);
            level.setCell(9, 10, 5);
            level.setCell(10, 10, 1);

            level.setCell(1, 7, 8);
            level.setCell(1, 8, 10);
            level.setCell(1, 9, 2);

            level.setCell(4, 6, 4);
            level.setCell(6, 6, 1);


            Pooka p1 = new Pooka(level.X + 32, level.Y + 80);
            p1.spritesheet = PookaSheet;
            p1.animation = "walk";
            p1.soundbank = soundBank;

            Pooka p2 = new Pooka(level.X + 16, level.Y + 128);
            p2.spritesheet = PookaSheet;
            p2.animation = "walk";
            p2.soundbank = soundBank;

            Pooka p3 = new Pooka(level.X + 96, level.Y + 128);
            p3.spritesheet = PookaSheet;
            p3.animation = "walk";
            p3.soundbank = soundBank;

            Fygar f2 = new Fygar(level.X + 144, level.Y + 48);
            f2.spritesheet = FygarSheet;
            f2.animation = "walk";
            f2.soundbank = soundBank;

            Fygar f1 = new Fygar(level.X + 144, level.Y + 160);
            f1.spritesheet = FygarSheet;
            f1.animation = "walk";
            f1.soundbank = soundBank;

            control.addObject(p1);
            control.addObject(p2);
            control.addObject(p3);
            control.addObject(f1);
            control.addObject(f2);

            Rock r1 = new Rock(level.X + 32, level.Y + 32, MiscSheet);
            r1.animation = "rocksteady";
            r1.soundbank = soundBank;

            Rock r2 = new Rock(level.X + 112, level.Y + 48, MiscSheet);
            r2.animation = "rocksteady";
            r2.soundbank = soundBank;

            Rock r3 = new Rock(level.X + 160, level.Y + 112, MiscSheet);
            r3.animation = "rocksteady";
            r3.soundbank = soundBank;

            Rock r4 = new Rock(level.X + 48, level.Y + 144, MiscSheet);
            r4.animation = "rocksteady";
            r4.soundbank = soundBank;

            control.addObject(r1);
            control.addObject(r2);
            control.addObject(r3);
            control.addObject(r4);
        }
    }
}
